/**
 * File: SamMatchParser.java
 * Created by: mhaimel
 * Created on: Jul 6, 2010
 * CVS:  $Id: SamMatchParser.java 1.0 Jul 6, 2010 11:55:04 AM mhaimel Exp $
 */
package uk.ac.ebi.curtain.utils.match.sam;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import net.sf.samtools.AlignmentBlock;
import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.SAMRecordIterator;

import org.apache.commons.lang.StringUtils;

import uk.ac.ebi.curtain.utils.file.FileInfo;
import uk.ac.ebi.curtain.utils.io.ParseException;
import uk.ac.ebi.curtain.utils.io.impl.FileIO;
import uk.ac.ebi.curtain.utils.match.MatchDetails;
import uk.ac.ebi.curtain.utils.match.MatchEntryMapper;
import uk.ac.ebi.curtain.utils.match.MatchParser;
import uk.ac.ebi.velvet.model.Strand;

/**
 * @author mhaimel
 *
 */
public class SamMatchParser implements MatchParser {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void parse(FileInfo info, MatchEntryMapper mapper)throws ParseException {
		SAMFileReader reader = null;
		try {
			reader = new SAMFileReader(info.getFile());
			reader.setValidationStringency(SAMFileReader.ValidationStringency.SILENT);
			SAMRecordIterator iter = reader.iterator();
			while(iter.hasNext()){
				SAMRecord next = iter.next();
				List<AlignmentBlock> blocks = next.getAlignmentBlocks();
				if(blocks.isEmpty()){
					// unmapped 
					mapper.mapEntry(new SamMatchDetails(info, next));
				} else {
					int size = blocks.size();
					for(int i = 0; i < size; ++i){
						mapper.mapEntry(new SamMatchDetails(info, next,i));
					}
				}
			}
		} finally{
			FileIO.closeQuietly(reader);
		}			
	}
	
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void parse(BufferedReader in, FileInfo info, MatchEntryMapper mapper) throws IOException {
		throw new UnsupportedOperationException("Not implemented to read form reader!!!");
	}
	
	public static List<MatchDetails> wrapEntry(FileInfo info,SAMRecord next){ // TO be usable from outside
		List<AlignmentBlock> blocks = next.getAlignmentBlocks();
		if(blocks.isEmpty()){
			// unmapped 
			return Arrays.asList(new MatchDetails[]{new SamMatchDetails(info, next)});
		} 
		int size = blocks.size();
		List<MatchDetails> list = new ArrayList<MatchDetails>(size);
		for(int i = 0; i < size; ++i){
			list.add(new SamMatchDetails(info, next,i));
		}
		return list;
	}
	
	public static class SamMatchDetails extends MatchDetails{
		private final SAMRecord samRec;

		public SamMatchDetails(FileInfo fileInfo, SAMRecord samRec, int alignBlock) {
			this(fileInfo,samRec);
			this.setAlignBlockNumber(alignBlock);
		}
		public SamMatchDetails(FileInfo fileInfo, SAMRecord samRec) {
			super(fileInfo, null,null, new int[0]);
			this.samRec = samRec;
		}
		@Override
		public String getReference() {
			return this.samRec.getReferenceName();
		}
		@Override
		public String getReadId() {return this.samRec.getReadName();}
		
		@Override
		public int getQualities(int i) {
			if(i > 0){
				throw new IndexOutOfBoundsException("Index: "+i+", Size: "+1);
			}
			return samRec.getMappingQuality();
		}
		@Override
		protected int[] getQualityArray() {
			return new int[]{samRec.getMappingQuality()};
		}
		@Override
		public int getQualitiesLength() { return 1; }
		
		@Override public void setReferenceLocation(Integer referenceLocation) {throw new UnsupportedOperationException();}
		@Override public int getReferenceLocation() { return getAlignBlocks() > 0?getAlignBlock().getReferenceStart():0;}
		
		@Override public void setReadLocation(Integer readLocation) {}
		@Override public int getReadLocation() {return getAlignBlocks() > 0?getAlignBlock().getReadStart():0;}
		@Override public void setAlignLength(Integer alignLength) {}
		@Override public int getAlignLength() {return getAlignBlocks() > 0?getAlignBlock().getLength():0;}
		
		public AlignmentBlock getAlignBlock(){
			return getAlignBlocks() > 0?samRec.getAlignmentBlocks().get(getAlignBlockNumber()):null;}

		@Override public void setAlignBlocks(Integer alignBlocks) {throw new UnsupportedOperationException();};
		@Override public int getAlignBlocks() {return samRec.getAlignmentBlocks().size();};
		
		@Override public void setPaired(boolean paired) {throw new UnsupportedOperationException();};
		@Override public boolean getPaired() {return this.samRec.getReadPairedFlag();};
		
		@Override public void setMapped(boolean mapped) {throw new UnsupportedOperationException();};
		@Override public boolean getMapped() {return !samRec.getReadUnmappedFlag();};
		
		@Override public void setOtherMapped(boolean otherMapped) {throw new UnsupportedOperationException();};
		@Override public boolean getOtherMapped() {return !samRec.getMateUnmappedFlag();};
		
		@Override public void setProperPaired(boolean paired) {throw new UnsupportedOperationException();};
		@Override public boolean getProperPaired() {return samRec.getProperPairFlag();};
		
		@Override public void setAlignOrientation(Strand alignOrientation) {throw new UnsupportedOperationException();};
		@Override public Strand getAlignOrientation() {return samRec.getReadNegativeStrandFlag()?Strand.REV:Strand.FWD;};

		@Override public void setReadLength(Integer readLength) {throw new UnsupportedOperationException();};
		@Override public int getReadLength() {return samRec.getReadLength();}
		
		@Override public void setReadOrientation(Strand readOrientation) {throw new UnsupportedOperationException();};
		@Override public Strand getReadOrientation() {return samRec.getFirstOfPairFlag()?Strand.FWD:Strand.REV;};

		@Override public void setDifferentContig(boolean differentContig) {throw new UnsupportedOperationException();};
		@Override public boolean getDifferentContig() {return !StringUtils.equals(samRec.getMateReferenceName(),getReference());};
		
		@Override
		public void setRepeatMarker(boolean repeatMark) {throw new UnsupportedOperationException();}
		@Override
		public boolean getRepeatMarker() {
			boolean repeat = false;
			Object rep = samRec.getAttribute("XT");
			if(null != rep && rep.toString().length() > 0){
				switch (rep.toString().charAt(0)) {
				case 'R': repeat = true; break; // Repeat
				case 'U': break; // uniq 
				case 'N': break; // Repeat
				case 'M': break; // Mate-sw
				default: break;
				}
			}
			return repeat;
		}
		
		@Override public void addAlternatives(AlternativeMatch alternative) {throw new UnsupportedOperationException();}
		@Override public void addAlternatives(Collection<AlternativeMatch> altMatches) {throw new UnsupportedOperationException();}
		@Override public boolean hasAlternatives() {return null != samRec.getAttribute("XA");}
		@Override
		public AlternativeMatch[] getAlternatives() {
			Object alt = samRec.getAttribute("XA");
			List<AlternativeMatch> altMatches = new LinkedList<AlternativeMatch>();
			if(null != alt){
				String[] arr = alt.toString().split(";");
				for(String s : arr){
					String[] cArr = s.split(",");
					String cId = cArr[0];
					String tmp = cArr[1];
					if(tmp.startsWith("+")){
						tmp = tmp.substring(1);
					}
					Integer mPos = Integer.valueOf(tmp);
//					Integer nucDiff = Integer.valueOf(cArr[3]);
					AlternativeMatch a = new AlternativeMatch(cId,getQualityArray());
					// quick hack - should do the job
					a.setAlignLength(getReadLength());
					a.setReadOrientation(getReadOrientation());
					a.setAlignOrientation(mPos > 0?Strand.FWD:Strand.REV);
					a.setReferenceLocation(Math.abs(mPos));
					a.setReadLocation(0);
					a.setRepeatMarker(getRepeatMarker());
					altMatches.add(a);
				}
			}
			return altMatches.toArray(new AlternativeMatch[0]);
		}

		public void setReadSequence(String sequence) {};
		public String getSequence() {return samRec.getReadString();};	
	}
	
//	public class SamAlternativeMatch extends AlternativeMatch{
//
//		public SamAlternativeMatch(String reference) {
//			super(reference);
//		}
//		
//		@Override
//		public void setAlignLength(Integer alignLength) {
//			super.setAlignLength(alignLength);
//		}
//		@Override
//		public void setAlignOrientation(Strand alignOrientation) {
//			super.setAlignOrientation(alignOrientation);
//		}
//		@Override
//		public void setReadLocation(Integer readLocation) {
//			super.setReadLocation(readLocation);
//		}
//		@Override
//		public void setReadOrientation(Strand readOrientation) {
//			super.setReadOrientation(readOrientation);
//		}
//		@Override
//		public void setReferenceLocation(Integer referenceLocation) {
//			super.setReferenceLocation(referenceLocation);
//		}
//		@Override
//		public void setRepeatMarker(boolean repeatMarker) {
//			super.setRepeatMarker(repeatMarker);
//		}
//		
//	}
	
}








